//To-do: remove debugging
#include <stdio.h>

/**
 * AppleIIGo
 * SmartPort class
 * (C) 2008 by Marc S. Ressl (mressl@umich.edu)
 * Released under the GPL
 */

#include "SmartPort.h"

SmartPort::SmartPort() : DiskPort() {
	driveList = new List();
	
	reset();
}

SmartPort::~SmartPort() {
	delete driveList;
}

void SmartPort::reset() {
	for (UINT32 index = 0; index < SMARTPORT_UNITNUM; index++)
		unit[index] = 0;
	
	driveList->setStart();
	
	isPhase0 = false;
	isDriveEnabled = false;
	
	isReq = false;
	isAck = true;
	isReply = false;
	isSending = false;
	isReceiving = false;
}

void SmartPort::connectDrive(SmartPortUnit * drive) {
	driveList->insert(drive);
}

void SmartPort::setPhaseRegister(UINT32 value) {
	isPhase0 = getBit(value, PHASE0);
	updateReq();
}

void SmartPort::setDriveEnabled(bool value) {
	isDriveEnabled = value;
	updateReq();
}

void SmartPort::updateReq() {
	bool isNextReq = isPhase0 && isDriveEnabled;
	if (isNextReq == isReq)
		return;
	isReq = isNextReq;
	
	if (isReq) {
//		printf("isReq\n");
		if (isReply) {
			writePacket();
			isSending = true;
		} else
			isReceiving = true;
		isReply = false;
		isPacketData = false;
		
		shiftRegister = 0;
		shiftRegisterBitNum = 0;
		
		packetBufferIndex = 0;
		if (isReceiving)
			packetBufferByteNum = 0;
	} else {
//		printf("\n");
//		printf("!isReq\n");
		isReceiving = false;
		isSending = false;
		
		// To-do: remove immediate response
//		if (!isAck)
//			printf("isAck\n");
		isAck = true;
	}
}

bool SmartPort::senseInput() {
	// To-Do: reset isAck after some time
	return isAck;
}

void SmartPort::updateWriteBitNum(UINT32 * value) {
}

void SmartPort::sendBit(bool value) {
	if (!isReceiving)
		return;
	
	shiftRegister <<= 1;
	shiftRegister |= value;
	
	if (!(shiftRegister & 0x80))
		return;
	
	UINT8 dataRegister = shiftRegister;
	shiftRegister = 0;
	
//	printf("W%02x ", dataRegister);
	
	if (!isPacketData) {
		if (dataRegister != 0xc3)
			return;
		isPacketData = true;
	}
	
	if (packetBufferByteNum >= SMARTPORTBUFFER_BYTENUM)
		return;
	
	packetBuffer[packetBufferByteNum++] = dataRegister;
	if (dataRegister == 0xc8)
		readPacket();
}

void SmartPort::updateReadBitNum(UINT32 * value) {
}

bool SmartPort::receiveBit() {
	if (!isSending)
		return false;
	
	if (!shiftRegisterBitNum) {
		shiftRegisterBitNum = 8;
		if (packetBufferIndex < packetBufferByteNum)
			shiftRegister = packetBuffer[packetBufferIndex++];
		else {
//			if (isAck)
//				printf("!isAck\n");
			isAck = false;
		}
//		printf("R%02x ", shiftRegister);
	}
	shiftRegisterBitNum--;
	
	bool bit = shiftRegister & 0x80;
	shiftRegister <<= 1;
	
	return bit;
}

void SmartPort::discardBits(UINT32 bitNum) {
}

void SmartPort::writePacketSync() {
	packetBuffer[packetBufferIndex++] = 0xff;
	packetBuffer[packetBufferIndex++] = 0x3f;
	packetBuffer[packetBufferIndex++] = 0xcf;
	packetBuffer[packetBufferIndex++] = 0xf3;
	packetBuffer[packetBufferIndex++] = 0xfc;
	packetBuffer[packetBufferIndex++] = 0xff;
}

void SmartPort::writePacketValue(UINT8 value) {
	packetBuffer[packetBufferIndex++] = value | 0x80;
}

UINT8 SmartPort::readPacketValue() {
	return packetBuffer[packetBufferIndex++] & 0x7f;
}

void SmartPort::writePacketGroup(UINT32 byteNum) {
	UINT32 packetBufferBits8Index = packetBufferIndex++;
	UINT8 bits8 = 0;
	
	for (UINT32 index = 0; index < byteNum; index++) {
		UINT8 value = packet.data[packet.dataByteNum++];
		writePacketValue(value);
		bits8 |= (value & 0x80) >> index;
	}
	
	packetBuffer[packetBufferBits8Index] = (bits8 >> 1) | 0x80;
}

void SmartPort::readPacketGroup(UINT32 byteNum) {
	UINT8 bits8 = readPacketValue() << 1;
	
	for (UINT32 index = 0; index < byteNum; index++) {
		packet.data[packet.dataByteNum++] = (bits8 & 0x80) | readPacketValue();
		bits8 <<= 1;
	}
}

void SmartPort::writePacket() {
	if (packet.dataByteNum > SMARTPORTPACKET_DATAMAXBYTENUM)
		return;
	
	UINT8 packetOddByteNum = packet.dataByteNum % 7;
	UINT8 packetGroupNum = packet.dataByteNum / 7;
	
	packetBufferIndex = 0;
	writePacketSync();
	UINT32 packetBufferHeaderIndex = packetBufferIndex;
	writePacketValue(0xc3);
	writePacketValue(packet.destinationId);
	writePacketValue(packet.sourceId);
	writePacketValue(packet.type);
	writePacketValue(packet.auxType);
	writePacketValue(packet.status);
	writePacketValue(packetOddByteNum);
	writePacketValue(packetGroupNum);
	
	packet.dataByteNum = 0;
	if (packetOddByteNum)
		writePacketGroup(packetOddByteNum);
	for (UINT32 index = 0; index < packetGroupNum; index++)
		writePacketGroup(7);
	
	UINT8 checksum = 0;
	for (UINT32 index = 1; index < SMARTPORTPACKET_HEADERBYTENUM; index++)
		checksum ^= packetBuffer[packetBufferHeaderIndex + index];
	for (UINT32 index = 0; index < packet.dataByteNum; index++)
		checksum ^= packet.data[index];
	
	writePacketValue(checksum | 0xaa);
	writePacketValue((checksum >> 1) | 0xaa);
	writePacketValue(0xc8);
	
	packetBufferByteNum = packetBufferIndex;
}

void SmartPort::readPacket() {
	if (packetBufferByteNum < SMARTPORTPACKET_HEADERBYTENUM)
		return;
	
	packetBufferIndex = 0;
	readPacketValue();
	packet.destinationId = readPacketValue();
	packet.sourceId = readPacketValue();
	packet.type = readPacketValue();
	packet.auxType = readPacketValue();
	packet.status = readPacketValue();
	UINT8 packetOddByteNum = readPacketValue();
	UINT8 packetGroupNum = readPacketValue();
	
	if (packetOddByteNum > 0x06)
		return;
	if (packetGroupNum > 0x6d)
		return;
	if (packetBufferByteNum != (SMARTPORTPACKET_HEADERBYTENUM +
								packetOddByteNum + 1 +
								packetGroupNum * 8 + 
								SMARTPORTPACKET_FOOTERBYTENUM))
		return;
	
	packet.dataByteNum = 0;
	if (packetOddByteNum)
		readPacketGroup(packetOddByteNum);
	for (UINT32 index = 0; index < packetGroupNum; index++)
		readPacketGroup(7);
	
	UINT8 checksum = 0;
	for (UINT32 index = 1; index < SMARTPORTPACKET_HEADERBYTENUM; index++)
		checksum ^= packetBuffer[index];
	for (UINT32 index = 0; index < packet.dataByteNum; index++)
		checksum ^= packet.data[index];
	
	UINT8 packetChecksum = readPacketValue() | 0x80;
	packetChecksum &= (readPacketValue() << 1) | 1;
	if (packetChecksum != checksum)
		return;
	
	if (readPacketValue() != (0xc8 & 0x7f))
		return;
	
	UINT32 action;
	if (!unit[packet.destinationId])
		action = initUnit();
	else
		action = unit[packet.destinationId]->sendPacket(&packet);
	
	isReply = (action == SMARTPORTACTION_REPLYPACKET);
	
	if (action == SMARTPORTACTION_NOREPLY)
		return;
	
//	if (isAck)
//		printf("!isAck\n");
	isAck = false;
}

UINT32 SmartPort::initUnit() {
	if (packet.type != SMARTPORTPACKETTYPE_COMMAND)
		return SMARTPORTACTION_NOREPLY;
	
	if (!packet.dataByteNum)
		return SMARTPORTACTION_NOREPLY;
	
	UINT8 command = packet.data[0];
	if (command != SMARTPORTCOMMAND_INIT)
		return SMARTPORTACTION_NOREPLY;
	if (!driveList->isNext())
		return SMARTPORTACTION_NOREPLY;
	
	unit[packet.destinationId] = (SmartPortUnit *) driveList->get();
	driveList->next();
	
	packet.sourceId = packet.destinationId;
	packet.destinationId = 0;
	packet.type = SMARTPORTPACKETTYPE_STATUS;
	packet.auxType = 0;
	packet.status = SMARTPORTERROR_NOERROR;
	packet.dataByteNum = 0;
	
	return SMARTPORTACTION_REPLYPACKET;
}
